#pragma once
#include "Vector3.h"
#include "Vector2.h"
using namespace Math;

#include <vector>
using namespace std;

//////////////////////////////////////////////////////////////////////////////
// Forward Declarations
//////////////////////////////////////////////////////////////////////////////
class TriVertex;
class TriFace;
class SubdivisionInterpolation;


//////////////////////////////////////////////////////////////////////////////
// Types
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
// TriEdge
//
// Description:
//      Edge class for the triangular mesh.
//////////////////////////////////////////////////////////////////////////////
class TriEdge
{
public:
   enum Degenerate{NONE=0, VERTEX};

protected:
   int id;

   bool marked;
   bool feature;

   // Topology info
   TriEdge* next;
   TriEdge* twin;
   TriVertex* orig;
   TriFace* face;
   Degenerate degenerate;

   // Geometry info
   Vector3 u;
   Real len0;
   bool reflex;
   Real area0;

   // Ownership (for absolute edges, not DCEL)
   TriFace *owner;

public:
   TriEdge(void)
   {
      Initialize();
   }

   ~TriEdge(void){}

   void Initialize(void);

   // Data get functions
   int Id(void) const
   {
      return id;
   }

   bool Marked(void) const
   {
      return marked;
   }

   bool IsFeature(void) const
   {
      return feature;
   }

   //Topology get functions
   TriEdge* Prev(void) const
   {
      return next->Next();
   }

   TriEdge* Next(void) const
   {
      return next;
   }

   TriEdge* Twin(void) const
   {
      return twin;
   }

   TriVertex* Origin(void) const
   {
      return orig;
   }

   TriVertex* Head(void) const
   {
      return next->Origin();
   }

   TriFace* AdjFace(void) const
   {
      return face;
   }

   Degenerate DegenerateMode(void) const
   {
      return degenerate;
   }

   bool IsBoundary(void) const
   {
      return Twin()==NULL;
   }

   bool Contains(TriVertex *v) const
   {
      return Origin() == v || Head() == v;
   }

   bool IsAdjacentTo(const TriEdge *other) const
   {
      return other->Contains(Origin()) || other->Contains(Head());
   }

   bool IsAdjacentTo(const TriFace *other) const;

   TriFace *Owner(void) const
   {
      return owner;
   }

   // Geometry get functions
   const Vector3& Direction(void) const
   {
      return u;
   }

   Real LengthInit(void) const
   {
      return len0;
   }

   Real Length(void) const;

   bool Reflex(void) const
   {
      return reflex;
   }

   Real Area0(void) const
   {
      return area0;
   }

   // Set functions
   void SetId(int i)
   {
      id=i;
   }

   void Mark(void)
   {
      marked=true;
   }

   void Unmark(void)
   {
      marked=false;
   }

   void SetFeature(bool f)
   {
      feature=f;
   }

   // Topology set functions
   void SetNext(TriEdge* n)
   {
      next=n;
   }

   void SetTwin(TriEdge* t)
   {
      twin=t;
   }

   void SetOrigin(TriVertex* o)
   {
      orig=o;
   }

   void SetAdjFace(TriFace* f)
   {
      face=f;
   }



   void CheckBoundary(void);

   void CheckDegeneracies(void)
   {
      if(Origin() == Head())
      {
         degenerate=VERTEX;
      }
   }

#ifdef _DEBUG
   bool VerifyTopology( int pos1, int pos2 ) const;
   bool VerifyGeometry(int pos1, int pos2) const;
#endif

   void SetOwner(TriFace *f)
   {
      owner = f;
   }

   //Geometry functions
   void ComputeDirectionLength(void);

   void ComputeDirectionLengthTwin(void);

   void ComputeConvexity(void);

   void ComputeConvexityTwin(void);

   void ComputeGeometry(void);

   void ComputeGeometryTwins(void);

   void ComputeArea(void);

   void SetLengthInit(Real l)
   {
      len0=l;
   }

   void SetDirection(const Vector3& dir)
   {
      u=dir;
      u.normalize();
   }

   void SetConvexity(bool r)
   {
      reflex=r;
   }

   //Interpolation from deformation field
   bool IsFixed(void) const;

   //Subdivision
   Vector3 GetPosSubdivision(void) const;
   Vector2 GetPosUVSubdivision(void) const;

   Vector3 GetPosSubdivisionWithWeights(const vector< vector<Real> >& win, vector< vector<Real> >& wout, int id, bool useBaryc);

   SubdivisionInterpolation *GetInterpSubdivision(Vector3& pos) const;

   void GetSubdivisionGovernors(vector<TriVertex*>& verts, vector<Real>& weights) const;

   //Collision detection
   Real MaxCollisionTime(void) const;


   bool operator< (const TriEdge &other){
      return id < other.Id();
   }
};



